﻿using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Tensop.Net.Core
{
    public class ConnectionFactory
    {
        Queue<ConnectionContext>            Pool;
        int                                 maxConnection;
        ISocketAsyncEventHandler            Handler;

        /// <summary>
        /// 初始化ConnectionFactory
        /// </summary>
        /// <param name="max">制定最大的连接数</param>
        /// <param name="handler">指定SocketAsyncEventArgs处理器</param>
        public ConnectionFactory(int max, ISocketAsyncEventHandler handler)
        {
            Pool = new Queue<ConnectionContext>( max );
            maxConnection = max;
            Handler = handler ?? throw new NullReferenceException();
        }

        /// <summary>
        /// 初始化连接池
        /// </summary>
        public void Init()
        {
            for ( int i = 0; i < maxConnection; i++ )
            {
                Pool.Enqueue( new ConnectionContext( i, IO_Completed ) );
            }
        }

        private void IO_Completed(object sender, SocketAsyncEventArgs e)
        {
            switch ( e.LastOperation )
            {
                case SocketAsyncOperation.Accept:
                    Handler.Accept( e );
                    break;
                case SocketAsyncOperation.Disconnect:
                    Handler.Disconnect( e );
                    break;
                case SocketAsyncOperation.Receive:
                    Handler.Receive( e );
                    Receive( e.UserToken as ConnectionContext );
                    break;
                case SocketAsyncOperation.Send:
                    Handler.Send( e );
                    break;
            }
        }

        /// <summary>
        /// 返回一个未绑定连接的连接上下文
        /// </summary>
        /// <returns>返回一个不为空的连接上下文</returns>
        public ConnectionContext Pop()
        {
            lock ( Pool )
            {
                if ( Pool.Count >= 1 )
                    return Pool.Dequeue();
                else
                    return null;
            }
        }

        /// <summary>
        /// 回收一个解除套接字绑定的连接上下文
        /// </summary>
        /// <param name="cc">指定要被回收的连接上下文</param>
        public void Push(ConnectionContext cc)
        {
            lock ( Pool )
            {
                Pool.Enqueue( cc );
            }
        }

        /// <summary>
        /// 获取当前连接池可用的连接数
        /// </summary>
        public int Count { get => Pool.Count; }

        /// <summary>
        /// 将一个套接字与一个连接上下文绑定，并开始接收数据操作
        /// </summary>
        /// <param name="cc">指定的非空连接上下文</param>
        /// <param name="socket">指定的套接字</param>
        /// <returns>返回绑定完成之后的连接上下文</returns>
        public ConnectionContext this[ConnectionContext cc, Socket socket] {
            get {
                UnBundle( cc );
                cc.Client = socket;
                return cc;
            }
        }

        /// <summary>
        /// 将指定的连接上下文与目前的 套接字解绑以便处理下一个连接
        /// </summary>
        /// <param name="cc">指定的连接上下文</param>
        /// <returns>返回解绑之后的连接上下文</returns>
        public ConnectionContext this[ConnectionContext cc] {
            get {
                UnBundle( cc );
                return cc;
            }
        }

        private void UnBundle(ConnectionContext cc)
        {
            if ( cc.Client != null )
            {
                try
                {
                    cc.Client.Shutdown( SocketShutdown.Both );
                    cc.Client.Disconnect( true );
                }
                catch { }
                cc.Client.Dispose();
                cc.Client = null;
            }
            cc.RecvBuffer.Clear();
            cc.SendBuffer.Clear();
        }

        /// <summary>
        /// 指定一个已绑定可用套接字的连接上下文进行数据接收
        /// </summary>
        /// <param name="cc">指定的连接上下文</param>
        public void Receive(ConnectionContext cc)
        {
            if ( !cc.Client.ReceiveAsync( cc.Reader ) )
            {
                if ( cc.Client.Connected )
                    using ( var reader = new NetworkStream( cc.Client ) )
                    {
                        if ( cc.State == ( ConnectionContextOperation.Cache |
                                        ConnectionContextOperation.ReceiveOp ) )
                        {
                            var length = new byte[4];
                            reader.Read( length, 0, 4 );
                            cc.ContentLength = BitConverter.ToInt32( length, 0 );

                            cc.State = ( ConnectionContextOperation.Transmit |
                                             ConnectionContextOperation.ReceiveOp );
                        }
                        else if ( cc.State == ( ConnectionContextOperation.Transmit |
                                             ConnectionContextOperation.ReceiveOp ) )
                        {
                            for ( ; cc.ContentLength > 0; cc.ContentLength-- )
                            {
                                var temp = reader.ReadByte();
                                if ( temp == -1 )
                                    return;
                                cc.RecvBuffer.Add( (byte) temp );
                            }
                        }
                        Handler.Receive( cc.Reader );
                    }
            }
        }

        /// <summary>
        /// 指定一个已绑定可用套接字的连接上下文发送数据，发送标头帧
        /// </summary>
        /// <param name="cc">指定的连接上下文</param>
        /// <param name="data">指定要发送的数据</param>
        public void Send(ConnectionContext cc, byte[] data)
        {
            if ( cc.SendBuffer == null )
                cc.SendBuffer = new List<byte>( 4096 );
            cc.SendBuffer.AddRange( data );
            cc.State = ConnectionContextOperation.Cache | ConnectionContextOperation.SendOp;
            cc.Sender.BufferList.Add( new ArraySegment<byte>( BitConverter.GetBytes( data.Length ) ) );
            cc.Sender.SetBuffer( 0, 4 );
        }

        /// <summary>
        /// 指定一个已绑定可用套接字的连接上下文，完成发送操作
        /// </summary>
        /// <param name="cc">指定的连接上下文</param>
        public void Send(ConnectionContext cc)
        {
            if ( cc.SendBuffer != null &&
                cc.SendBuffer.Count > 0 &&
                cc.State == ( ConnectionContextOperation.Cache |
                             ConnectionContextOperation.SendOp ) )
            {
                cc.State = ConnectionContextOperation.Transmit | ConnectionContextOperation.SendOp;
                if ( cc.Sender.Buffer.Length >= cc.SendBuffer.Count )
                {
                    Array.Copy( cc.SendBuffer.ToArray(), 0, cc.Sender.Buffer, 0, cc.SendBuffer.Count );
                    cc.Sender.SetBuffer( 0, cc.SendBuffer.Count );
                }
                else
                {
                    cc.Sender.SetBuffer( cc.SendBuffer.ToArray(), 0, cc.SendBuffer.Count );
                }
                if ( !cc.Client.SendAsync( cc.Sender ) )
                {
                    if ( cc.Client.Connected )
                    {
                        using ( var writer = new NetworkStream( cc.Client ) )
                        {
                            writer.Write( cc.SendBuffer.ToArray(), 0, cc.SendBuffer.Count );
                        }
                    }
                }
                cc.SendBuffer.Clear();
                cc.State = ConnectionContextOperation.Cache | ConnectionContextOperation.ReceiveOp;
            }
        }
    }
}
